<script lang="ts" setup>
import { computed, nextTick, reactive, ref, unref, useAttrs, watchEffect } from 'vue'
import { Checkbox, Divider, Popover, Tooltip } from 'ant-design-vue'
import type { CheckboxChangeEvent, CheckboxValueType } from 'ant-design-vue/lib/checkbox/interface'
import { DragOutlined, SettingOutlined } from '@ant-design/icons-vue'
import { cloneDeep, omit } from 'lodash-es'
import Sortablejs from 'sortablejs'
import type Sortable from 'sortablejs'
import type { BasicColumn, BasicTableProps, ColumnChangeParam } from '../../types/table'
import { useTableContext } from '../../hooks/useTableContext'
import { Icon } from '@/components/Icon'
import { ScrollContainer } from '@/components/Container'
import { useI18n } from '@/hooks/web/useI18n'
import { useDesign } from '@/hooks/web/useDesign'

import { isFunction, isNil } from '@/utils/is'
import { getPopupContainer as getParentContainer } from '@/utils'

interface State {
  checkAll: boolean
  isInit?: boolean
  checkedList: string[]
  defaultCheckList: string[]
}

interface Options {
  label: string
  value: string
  fixed?: boolean | 'left' | 'right'
}

defineOptions({ name: 'ColumnSetting' })

const emit = defineEmits(['columns-change'])

const CheckboxGroup = Checkbox.Group
const attrs = useAttrs()

const { t } = useI18n()
const table = useTableContext()

const defaultRowSelection = omit(table.getRowSelection(), 'selectedRowKeys')
let inited = false
// 是否当前的setColums触发的
let isSetColumnsFromThis = false
// 是否当前组件触发的setProps
let isSetPropsFromThis = false

const cachePlainOptions = ref<Options[]>([])
const plainOptions = ref<Options[] | any>([])

const plainSortOptions = ref<Options[]>([])

const columnListRef = ref(null)

const state = reactive<State>({
  checkAll: true,
  checkedList: [],
  defaultCheckList: [],
})

/** 缓存初始化props */
let cacheTableProps: Partial<BasicTableProps<any>> = {}
const checkIndex = ref(false)
const checkSelect = ref(false)

const { prefixCls } = useDesign('basic-column-setting')

const getValues = computed(() => {
  return unref(table?.getBindValues) || {}
})

watchEffect(() => {
  const columns = table.getColumns()
  setTimeout(() => {
    if (isSetColumnsFromThis)
      isSetColumnsFromThis = false
    else if (columns.length)
      init()
  }, 0)
})

watchEffect(() => {
  const values = unref(getValues)
  if (isSetPropsFromThis)
    isSetPropsFromThis = false
  else
    cacheTableProps = cloneDeep(values)

  checkIndex.value = !!values.showIndexColumn
  checkSelect.value = !!values.rowSelection
})

function getColumns() {
  const ret: Options[] = []
  table.getColumns({ ignoreIndex: true, ignoreAction: true }).forEach((item) => {
    ret.push({
      label: (item.title as string) || (item.customTitle as string),
      value: (item.dataIndex || item.title) as string,
      ...item,
    })
  })
  return ret
}

async function init(isReset = false) {
  // Sortablejs存在bug，不知道在哪个步骤中会向el append了一个childNode，因此这里先清空childNode
  // 有可能复现上述问题的操作：拖拽一个元素，快速的上下移动，最后放到最后的位置中松手
  plainOptions.value = []
  const columnListEl = unref(columnListRef)
  if (columnListEl && (columnListEl as any).$el) {
    const el = (columnListEl as any).$el as Element
    Array.from(el.children).forEach(item => el.removeChild(item))
  }
  await nextTick()
  const columns = isReset ? cloneDeep(cachePlainOptions.value) : getColumns()

  const checkList = table
    .getColumns({ ignoreAction: true, ignoreIndex: true })
    .map((item) => {
      if (item.defaultHidden)
        return ''

      return item.dataIndex || item.title
    })
    .filter(Boolean) as string[]
  plainOptions.value = columns
  plainSortOptions.value = columns
  // 更新缓存配置
  table.setCacheColumns?.(columns)
  !isReset && (cachePlainOptions.value = cloneDeep(columns))
  state.defaultCheckList = checkList
  state.checkedList = checkList
  // 是否列展示全选
  state.checkAll = checkList.length === columns.length
  inited = false
  handleOpenChange()
}

// checkAll change
function onCheckAllChange(e: CheckboxChangeEvent) {
  const checkList = plainSortOptions.value.map(item => item.value)
  plainSortOptions.value.forEach(
    item => ((item as BasicColumn).defaultHidden = !e.target.checked),
  )
  if (e.target.checked) {
    state.checkedList = checkList
    setColumns(checkList)
  }
  else {
    state.checkedList = []
    setColumns([])
  }
}

const indeterminate = computed(() => {
  const len = plainOptions.value.length
  const checkedLen = state.checkedList.length
  // unref(checkIndex) && checkedLen--;
  return checkedLen > 0 && checkedLen < len
})

// Trigger when check/uncheck a column
function onChange(checkedList: CheckboxValueType[]) {
  const len = plainSortOptions.value.length
  state.checkAll = checkedList.length === len
  const sortList = unref(plainSortOptions).map(item => item.value)
  checkedList.sort((prev, next) => {
    return sortList.indexOf(String(prev)) - sortList.indexOf(String(next))
  })
  unref(plainSortOptions).forEach((item) => {
    (item as BasicColumn).defaultHidden = !checkedList.includes(item.value)
  })
  setColumns(checkedList as string[])
}

let sortable: Sortable
let sortableOrder: string[] = []
// reset columns
function reset() {
  setColumns(cachePlainOptions.value)
  init(true)
  checkIndex.value = !!cacheTableProps.showIndexColumn
  checkSelect.value = !!cacheTableProps.rowSelection
  table.setProps({
    showIndexColumn: checkIndex.value,
    rowSelection: checkSelect.value ? defaultRowSelection : undefined,
  })
  sortable.sort(sortableOrder)
}

// Open the pop-up window for drag and drop initialization
function handleOpenChange() {
  if (inited)
    return
  nextTick(() => {
    const columnListEl = unref(columnListRef)
    if (!columnListEl)
      return
    const el = (columnListEl as any).$el
    if (!el)
      return
    // Drag and drop sort
    sortable = Sortablejs.create(unref(el), {
      animation: 500,
      delay: 400,
      delayOnTouchOnly: true,
      handle: '.table-column-drag-icon ',
      onEnd: (evt) => {
        const { oldIndex, newIndex } = evt
        if (isNil(oldIndex) || isNil(newIndex) || oldIndex === newIndex)
          return

        // Sort column
        const columns = cloneDeep(plainSortOptions.value)

        if (oldIndex > newIndex) {
          columns.splice(newIndex, 0, columns[oldIndex])
          columns.splice(oldIndex + 1, 1)
        }
        else {
          columns.splice(newIndex + 1, 0, columns[oldIndex])
          columns.splice(oldIndex, 1)
        }

        plainSortOptions.value = columns

        setColumns(columns.filter(item => state.checkedList.includes(item.value)))
      },
    })
    // 记录原始order 序列
    sortableOrder = sortable.toArray()
    inited = true
  })
}

// Control whether the serial number column is displayed
function handleIndexCheckChange(e: CheckboxChangeEvent) {
  isSetPropsFromThis = true
  isSetColumnsFromThis = true
  table.setProps({
    showIndexColumn: e.target.checked,
  })
}

// Control whether the check box is displayed
function handleSelectCheckChange(e: CheckboxChangeEvent) {
  isSetPropsFromThis = true
  isSetColumnsFromThis = true
  table.setProps({
    rowSelection: e.target.checked ? defaultRowSelection : undefined,
  })
}

function handleColumnFixed(item: BasicColumn, fixed?: 'left' | 'right') {
  if (!state.checkedList.includes(item.dataIndex as string))
    return

  const columns = getColumns().filter((c: BasicColumn) =>
    state.checkedList.includes(c.dataIndex as string),
  ) as BasicColumn[]
  const isFixed = item.fixed === fixed ? false : fixed
  const index = columns.findIndex(col => col.dataIndex === item.dataIndex)
  if (index !== -1)
    columns[index].fixed = isFixed

  item.fixed = isFixed

  if (isFixed && !item.width)
    item.width = 100

  updateSortOption(item)
  table.setCacheColumnsByField?.(item.dataIndex as string, { fixed: isFixed })
  setColumns(columns)
}

function setColumns(columns: BasicColumn[] | string[]) {
  isSetPropsFromThis = true
  isSetColumnsFromThis = true
  table.setColumns(columns)
  const data: ColumnChangeParam[] = unref(plainSortOptions).map((col) => {
    const open
      = columns.findIndex(
        (c: BasicColumn | string) =>
          c === col.value || (typeof c !== 'string' && c.dataIndex === col.value),
      ) !== -1
    return { dataIndex: col.value, fixed: col.fixed, open }
  })

  emit('columns-change', data)
}

function getPopupContainer() {
  return isFunction(attrs.getPopupContainer)
    ? attrs.getPopupContainer()
    : getParentContainer()
}

function updateSortOption(column: BasicColumn) {
  plainSortOptions.value.forEach((item) => {
    if (item.value === column.dataIndex)
      Object.assign(item, column)
  })
}
</script>

<template>
  <Tooltip placement="top">
    <template #title>
      <span>{{ t('component.table.settingColumn') }}</span>
    </template>
    <Popover
      placement="bottomLeft"
      trigger="click"
      :overlay-class-name="`${prefixCls}__cloumn-list`"
      :get-popup-container="getPopupContainer"
      @open-change="handleOpenChange"
    >
      <template #title>
        <div :class="`${prefixCls}__popover-title`">
          <Checkbox v-model:checked="state.checkAll" :indeterminate="indeterminate" @change="onCheckAllChange">
            {{ t('component.table.settingColumnShow') }}
          </Checkbox>

          <Checkbox v-model:checked="checkIndex" @change="handleIndexCheckChange">
            {{ t('component.table.settingIndexColumnShow') }}
          </Checkbox>

          <Checkbox v-model:checked="checkSelect" :disabled="!defaultRowSelection" @change="handleSelectCheckChange">
            {{ t('component.table.settingSelectColumnShow') }}
          </Checkbox>

          <a-button size="small" type="link" @click="reset">
            {{ t('common.resetText') }}
          </a-button>
        </div>
      </template>

      <template #content>
        <ScrollContainer>
          <CheckboxGroup ref="columnListRef" v-model:value="state.checkedList" @change="onChange">
            <template v-for="item in plainOptions" :key="item.value">
              <div v-if="!('ifShow' in item && !item.ifShow)" :class="`${prefixCls}__check-item`">
                <DragOutlined class="table-column-drag-icon" />
                <Checkbox :value="item.value">
                  {{ item.label }}
                </Checkbox>

                <Tooltip placement="bottomLeft" :mouse-leave-delay="0.4" :get-popup-container="getPopupContainer">
                  <template #title>
                    {{ t('component.table.settingFixedLeft') }}
                  </template>
                  <Icon
                    icon="line-md:arrow-align-left"
                    :class="[
                      `${prefixCls}__fixed-left`,
                      {
                        active: item.fixed === 'left',
                        disabled: !state.checkedList.includes(item.value),
                      },
                    ]"
                    @click="handleColumnFixed(item, 'left')"
                  />
                </Tooltip>
                <Divider type="vertical" />
                <Tooltip placement="bottomLeft" :mouse-leave-delay="0.4" :get-popup-container="getPopupContainer">
                  <template #title>
                    {{ t('component.table.settingFixedRight') }}
                  </template>
                  <Icon
                    icon="line-md:arrow-align-left"
                    :class="[
                      `${prefixCls}__fixed-right`,
                      {
                        active: item.fixed === 'right',
                        disabled: !state.checkedList.includes(item.value),
                      },
                    ]"
                    @click="handleColumnFixed(item, 'right')"
                  />
                </Tooltip>
              </div>
            </template>
          </CheckboxGroup>
        </ScrollContainer>
      </template>
      <SettingOutlined />
    </Popover>
  </Tooltip>
</template>

<style lang="less">
@prefix-cls: ~'@{namespace}-basic-column-setting';

.table-column-drag-icon {
  margin: 0 5px;
  cursor: move;
}

.@{prefix-cls} {
  &__popover-title {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: space-between;
  }

  &__check-item {
    display: flex;
    align-items: center;
    min-width: 100%;
    padding: 4px 16px 8px 0;

    .ant-checkbox-wrapper {
      width: 100%;

      &:hover {
        color: @primary-color;
      }
    }
  }

  &__fixed-left,
  &__fixed-right {
    color: rgb(0 0 0 / 45%);
    cursor: pointer;

    &.active,
    &:hover {
      color: @primary-color;
    }

    &.disabled {
      color: @disabled-color;
      cursor: not-allowed;
    }
  }

  &__fixed-right {
    transform: rotate(180deg);
  }

  &__cloumn-list {
    svg {
      width: 1em !important;
      height: 1em !important;
    }

    .ant-popover-inner-content {
      // max-height: 360px;
      padding-right: 0;
      padding-left: 0;
      // overflow: auto;
    }

    .ant-checkbox-group {
      display: inline-block;
      width: 100%;
      min-width: 260px;
      // flex-wrap: wrap;
    }

    .scrollbar {
      height: 220px;
    }
  }
}
</style>
